package org.misty.redis.autoconfigure.util;

import org.misty.redis.autoconfigure.filter.FilterConstants;
import org.misty.redis.autoconfigure.locker.LockerConstants;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;

/**
 * @author Misty on 2020-06-12
 */
public class BeanUtil {
    private BeanUtil() throws Exception {
        throw new Exception();
    }

    public static void uniqueBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
        if (registry.containsBeanDefinition(beanName)) {
            throw new IllegalStateException("BeanDefinition " + beanName + " exists: " + registry.getBeanDefinition(beanName));
        }
    }

    public static GenericBeanDefinition singletonBeanDefinition(Class<?> klass, boolean primary) {
        GenericBeanDefinition definition = new GenericBeanDefinition();
        definition.setBeanClass(klass);
        definition.setAutowireCandidate(true);
        definition.setScope(BeanDefinition.SCOPE_SINGLETON);
        definition.setPrimary(primary);
        return definition;
    }

    @Deprecated
    public static String getFilterName(String s) {
        return getBeanName(s, FilterConstants.BEAN_NAME_SUFFIX);
    }

    @Deprecated
    public static String getLockerName(String s) {
        return getBeanName(s, LockerConstants.BEAN_NAME_SUFFIX);
    }

    public static String getBeanName(String s, String suffix) {
        if (s.endsWith(suffix)) {
            return s;
        }
        return s + suffix;
    }

    public static String[] getPossibleBeanNames(String s, String suffix) {
        if (StringUtils.isEmpty(s)) return null;

        String camelize = Util.camelize(s);
        LinkedHashSet<String> names = new LinkedHashSet<>();
        names.add(s);
        names.add(getBeanName(s, suffix));
        names.add(camelize);
        names.add(getBeanName(camelize, suffix));
        return names.toArray(new String[0]);
    }

    /**
     * @param args String or Class
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(BeanFactory beanFactory, Class<T> requiredType, Object... args) {
        if (args != null) {
            BeansException exception = null;
            for (Object arg : args) {
                if (arg instanceof String) {
                    String beanName = (String) arg;
                    if (!beanFactory.containsBean(beanName)) continue;

                    if (beanFactory.isTypeMatch(beanName, requiredType)) {
                        return beanFactory.getBean(beanName, requiredType);
                    }
//                    Object optional = beanFactory.getBean(beanName);
//                    if (requiredType.isAssignableFrom(optional.getClass())) {
//                        return (T) optional;
//                    }
                } else if (arg instanceof Class) {
                    Class<?> klass = (Class<?>) arg;
                    if (!requiredType.isAssignableFrom(klass)) continue;

                    ObjectProvider<?> provider = beanFactory.getBeanProvider(klass);
                    try {
                        Object bean = provider.getIfAvailable();
                        if (bean != null) {
                            return (T) bean;
                        }
                    } catch (BeansException ex) {
                        exception = ex;
                    }
                }
            }
            if (exception != null) throw exception;
        }
        return null;
    }

    public static void main(String[] args) {
        Class<?> classMap = Map.class;
        Class<?> classHashMap = HashMap.class;

        System.out.println(classMap.isAssignableFrom(classHashMap));
    }
}
